#include "render.h"
#include "line.h"

#include "../states.h"
#include "../console.h"

#ifdef HAS_D3D
# include <d3dx9.h>
#endif

#include "../../Epsilon/Epsilon3D/OpenGL.h"
#include "../../Epsilon/Epsilon3D/viewport.h"

#include "shadows.h"


// Animated Illumination
float
AnimateLight (float time)
{
  static const short levels [] = { 4, 19, 6, 1, 18, 1, 8, 4, 8, 1 };

  static unsigned int peak        = 0;
  static float        step        = 0.0f;
  static float        first_peak  = 0.0f;
  static float        second_peak = 0.0f;
  static float        last_tick   = 0.0f;

  if (time > (last_tick + 50))
  {
    last_tick = time;

    if (! step)
    {
      first_peak = levels [peak];
      peak++;

      if (peak >= (sizeof levels / sizeof levels [0]))
        peak = 0;

      second_peak = levels [peak];

      step = (second_peak - first_peak) / 4;
    }

    first_peak += step;

    if ((step > 0 && first_peak > second_peak) || (step < 0 && second_peak > first_peak))
    {
      step = 0.0f;
      first_peak = second_peak;
    }
  }

  return (1.0f - (first_peak / 50.0f));
}

#include "../../Epsilon/STL.h"
#include "../../Epsilon/timer.h"
#include "../../Epsilon/Epsilon3D/rendercontext.h"

#include "level.h"
#include "room.h"
#include "sector.h"
#include "render_batch.h"
#include "util.h"

void begin_fog (DWORD Color, DWORD Mode);
void end_fog   (void);



void AnimateTextures (float time,
                      short xanim,
                      short yanim,
                      short& tx,
                      short& ty)
{
  const int mask = 63;

  tx = ((int)time);// & mask;
  ty = ((int)time);// & mask;
}

inline DWORD FtoDW (float f) { return *((DWORD *)&f); };

#include "../camera.h"

void DrawActors ( Underlight::Room* room,
                  const float&      time,
                  D3DMATERIAL9&     light_mat,
                  bool              texture = true )
{
  /* Do not draw actors if r_actors is false... */
  if (! cli_parse::states->r_actors)
    return;

  std::vector<Underlight::Sector*>::const_iterator sector   = room->sectors.begin ();
  std::vector<Underlight::Sector*>::const_iterator last_sec = room->sectors.end   ();

  /* Changes Alpha Func, Blend Func, Current Color, Depth Test, Color Material, and Polygon Front Face... */
  glPushAttrib       (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT | GL_POLYGON_BIT);
  glPushClientAttrib (GL_CLIENT_VERTEX_ARRAY_BIT); /* DrawBillboards changes these ptrs. */

//  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::True);
//  cli_parse::states->render_context->SetAlphaFunc (e3dRenderStates::Equal, 0.0f);
//  cli_parse::states->render_context->SetAlphaTest (e3dRenderStates::True);
//  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::False);

  glAlphaFunc (GL_GEQUAL, 0.9f);
  glEnable    (GL_ALPHA_TEST);
  glEnable    (GL_CULL_FACE);
  glEnable    (GL_DEPTH_TEST);

  /* Wind the polygons CCW... */
  glFrontFace (GL_CCW);

  if (texture) {
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable    (GL_BLEND);
  } else {
    glDepthFunc (GL_EQUAL);
  }

  glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
  glEnable        (GL_COLOR_MATERIAL);
  glColor4f       (1.0f, 1.0f, 1.0f, 1.0f);

  // Non Self-Illuminating Sectors First
  while (eSTL::IterValid (sector, last_sec)) {
    std::vector<cli_parse::Actor*>::const_iterator actor      = (*sector)->actors.begin ();
    std::vector<cli_parse::Actor*>::const_iterator last_actor = (*sector)->actors.end   ();

    while (eSTL::IterValid (actor, last_actor)) {
      if (! ((*sector)->flags & SECTOR_SELFILLUMINATING)) {
        void DrawBillboard (cli_parse::Actor* actor, float time, bool texture);
        DrawBillboard ((*actor), time, texture);
      }

      ++actor;
    }

    ++sector;
  }

  sector = room->sectors.begin ();

  glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT);
  glColor4f       ( light_mat.Ambient.r,
                      light_mat.Ambient.g,
                        light_mat.Ambient.b,
                          light_mat.Ambient.a );

  // Self-Illuminating Sectors Last
  while (eSTL::IterValid (sector, last_sec)) {
    std::vector<cli_parse::Actor*>::const_iterator actor      = (*sector)->actors.begin ();
    std::vector<cli_parse::Actor*>::const_iterator last_actor = (*sector)->actors.end   ();

    while (eSTL::IterValid (actor, last_actor)) {
      if (((*sector)->flags & SECTOR_SELFILLUMINATING)) {
        void DrawBillboard (cli_parse::Actor* actor, float time, bool texture);
        DrawBillboard ((*actor), time, texture);
      }

      ++actor;
    }

    ++sector;
  }

  glPopAttrib       ();
  glPopClientAttrib ();

//    cli_parse::states->render_context->SetAlphaTest (e3dRenderStates::False);
//    cli_parse::states->render_context->SetCullFaces (e3dRenderStates::True);
}

void
DrawShadowActors ( Underlight:: Room* room,
                   const float&       time,
                   bool               texture = true )
{
  float color [4];

  color [0] = 0.0f;
  color [1] = 0.0f;
  color [2] = 0.0f;
  color [3] = 1.0f;

  D3DMATERIAL9 light_mat;
  light_mat.Ambient.r = color [0];
  light_mat.Ambient.g = color [1];
  light_mat.Ambient.b = color [2];
  light_mat.Ambient.a = color [3];

  DrawActors (room, time, light_mat, texture);
}

#include "projectile.h"

void
DrawProjectiles (void)
{
  // Only draw the points in the forward renderer, it is a waste
  //   of filrate otherwise.
  if (cli_parse::states->r_deferred_mode != 0)
    return;

  glPushAttrib (GL_ALL_ATTRIB_BITS);//GL_CURRENT_BIT);
  glDisable    (GL_CULL_FACE);
  glPointSize  (100.0f);
  glColor4f    (1.0f, 1.0f, 1.0f, 1.0f);
  glEnable     (GL_POLYGON_OFFSET_FILL);
  glPolygonOffset (1.2f, 5.0f);
  glBegin      (GL_POINTS);

    for (int i = 0; i < cli_parse::GetNumProjectiles (); i++) {
      cli_parse::Projectile* projectile = cli_parse::GetProjectile (i);
      glVertex3f (projectile->origin.x, projectile->origin.y, projectile->origin.z);
    }

  glEnd       ();
  glPopAttrib ();
}

  extern void
  draw_background (int tex);

void
SetupTextures (cli_parse::RenderBatch* batch, const float& time)
{
  if (batch->type == 2 || batch->type == -2 || (! batch->type )) { // Zero = Wall
    if (batch->type)
      eTB_Printf ("Found a missed Texture!\n");

    if (batch->tex_frames) {
      int tex = ((batch->tex_id + batch->tex_frames - 
              ((int)(time * 10.0f) % batch->tex_frames)));
      ApplyTexture   (tex);
    } else {
      ApplyTexture   (batch->tex_id);
    }
  } else {              // Non-Zero = Flat (1 == Floor, -1 == Ceiling)
    ApplyFlatTexture (batch->tex_id);
  }
}

void
DrawDepthBatches ( Underlight::Room* room )
{
  glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

  std::vector<cli_parse::RenderBatch*>::const_iterator batch;
  std::vector<cli_parse::RenderBatch*>::const_iterator last_batch;

  enum {
    Static    = 0,
    Animated  = 1,
    NUM_STAGES
  };

  for (int stage = 0; stage < NUM_STAGES; stage++) {
    switch (stage) {
      case Static:
        batch      = room->batches.begin ();
        last_batch = room->batches.end   ();
        break;
      case Animated:
        batch      = room->animated_batches.begin ();
        last_batch = room->animated_batches.end   ();
        break;
    }

    while (batch != last_batch) {
      cli_parse::batch_processor.Draw (room->vcache, (*batch));

      ++batch;
    }
  }

  glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}

void
DrawStaticBatches ( Underlight::Room* room,
                    BatchList&        batches,
                    const float&      time )
{
  std::vector<cli_parse::RenderBatch*>::const_iterator batch      =
      batches.begin ();
  std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
      batches.end   ();

  while (eSTL::IterValid (batch, last_batch)) {
    if ((*batch)->animated != 0) {
      ++batch;
      continue;
    }

    SetupTextures ((*batch), time);

    cli_parse::batch_processor.Draw (room->vcache, (*batch));

    ++batch;
  }
}

void
DrawAnimatedFlatBatches ( Underlight::Room* room,
                          BatchList         batches,
                          const float&      time,
                          const float&      ms )
{
#ifdef HAS_D3D
  LPDIRECT3DDEVICE9 dev = NULL;

  if (e3dDirect3D::IsDevicePresent ())
    dev = e3dDirect3D::GetDev ();
#endif

  std::vector<cli_parse::RenderBatch*>::const_iterator batch =
      batches.begin ();
  std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
      batches.end   ();

#ifdef HAS_D3D
  if (dev)
    dev->SetTextureStageState (0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
#endif

    while (eSTL::IterValid (batch, last_batch)) {
      if ((! (*batch)->animated) || 
            ((*batch)->animated & 2)) {
        ++batch;
        continue;
      }

      SetupTextures ((*batch), time);

      if ((*batch)->type == 1 || (*batch)->type == -1) {
        short texture_map_u = 0;
        short texture_map_v = 0;

        //texture_map_u = (*batch)->xanim;
        //texture_map_v = (*batch)->yanim;

        AnimateTextures (ms, (*batch)->xanim,
                                        (*batch)->yanim,
                                        texture_map_u,
                                        texture_map_v);

        int anim_x = texture_map_u;
        int anim_y = texture_map_v;
//        anim_x <<= 16;
//        anim_y <<= 16;

//        float factor = (time * 3.6f) * 0.33f;

#ifdef HAS_D3D
        D3DXMATRIX matFloorTrans;
        D3DXMatrixIdentity (&matFloorTrans);
#endif

        if ((*batch)->surface) {
          Underlight::Line* baseline = (*batch)->surface->Baseline ();

          if (baseline) {
            if (baseline->x1 () != baseline->x2 ())
              anim_y = -anim_y;

            if (baseline->y1 () != baseline->y2 ())
              anim_x = -anim_x;
          }
        }

#ifdef HAS_D3D
        matFloorTrans._31 = anim_x / 10000.0f;
        matFloorTrans._32 = anim_y / 10000.0f;
//            matFloorTrans._31 = (factor * texture_map_u); // U
//            matFloorTrans._32 = (factor * texture_map_v); // V

        if (dev)
          dev->SetTransform (D3DTS_TEXTURE0, &matFloorTrans);
        else {
#endif
          glMatrixMode (GL_TEXTURE);

          glPushMatrix ();

          float sector_cos      = 0.0f;
          float sector_sin      = 1.0f;
          float sector_origin_x = 0.0f;
          float sector_origin_y = 0.0f;

         if ((*batch)->surface) {
          Underlight::Line* baseline = (*batch)->surface->Baseline ();
          if (baseline) {
             sector_cos      = -baseline->SINE;
             sector_sin      = baseline->COS;
             sector_origin_x = baseline->x1 ();
             sector_origin_y = baseline->y1 ();
    }
  }

  float sec_x = 1.0f;//(sector_cos - sector_sin);
  float sec_y = 1.0f;//(sector_cos + sector_sin);

        glTranslatef (-(float)anim_y * sec_y / 10000.0f, -((float)anim_x * sec_x) / 10000.0f, 0);

#ifdef HAS_D3D
        }
#endif
      }

      cli_parse::batch_processor.Draw (room->vcache, (*batch));

      glPopMatrix ();

      ++batch;
    }

#ifdef HAS_D3D
    if (dev)
      dev->SetTextureStageState (0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
    else
#endif
}

void
DrawAnimatedIlluminationBatches ( Underlight::Room* room,
                                  BatchList&        batches,
                                  const float&      time,
                                  D3DMATERIAL9&     light_mat )
{
#ifdef HAS_D3D
  LPDIRECT3DDEVICE9 dev = NULL;
#endif

  std::vector<cli_parse::RenderBatch*>::const_iterator batch =
      batches.begin ();
  std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
      batches.end   ();

#ifdef HAS_D3D
  if (e3dDirect3D::IsDevicePresent ())
    dev = e3dDirect3D::GetDev ();

  if (dev) {
    // Don't use vertex colors for sectors with animated illumination...
    dev->SetRenderState (D3DRS_COLORVERTEX,           false);
    dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
    dev->SetRenderState (D3DRS_LIGHTING,              true);

    dev->SetMaterial (&light_mat);
  } else {
#endif
    float color[4];

    color [0] = light_mat.Ambient.r;
    color [1] = light_mat.Ambient.g;
    color [2] = light_mat.Ambient.b;
    color [3] = light_mat.Ambient.a;

    glDisableClientState (GL_COLOR_ARRAY);
    glColorPointer       (4, GL_UNSIGNED_BYTE, 0, NULL); // Why is this necessary? :(

    glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); // Originally Ambient ONLY
    glEnable        (GL_COLOR_MATERIAL);

    glColor4f (light_mat.Ambient.r, light_mat.Ambient.g, light_mat.Ambient.b, light_mat.Ambient.a);


#ifdef HAS_D3D
  }
#endif

  while (eSTL::IterValid (batch, last_batch)) {
    if ((! (*batch)->animated) || 
          ((*batch)->animated & 1)) {
      ++batch;
      continue;
    }

    SetupTextures ((*batch), time);

    cli_parse::batch_processor.Draw (room->vcache, (*batch));

    ++batch;
  }

#ifdef HAS_D3D
  if (dev) {
    dev->SetRenderState (D3DRS_COLORVERTEX,           true);
    dev->SetRenderState (D3DRS_LIGHTING,              false);
    dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
  } else {
#endif
    //glEnableClientState (GL_COLOR_ARRAY);
#ifdef HAS_D3D
  }
#endif

  /// XXX: If we don't do this, animated illumination batches may affect
  ///        other batches in sectors with no lighting...   - HACK: FIX ME!
  glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
}

#include "../../Epsilon/Epsilon3D/OpenGL.h"

/* For periodic shadow updates... n > 0.0, n = 0 -> Always updated. */
float last_shadow_update = 0.0f;

#include "light_cam.h"

void
DrawRoom (Underlight::Room* room, const float& time, const float& ms, D3DMATERIAL9& light_mat)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  // Handle picking and skip the other rendering...
  if (states->cl_need_pick) {
    /* Picking is implemented using an image-space pixel-perfect GPU query,
         thus it MUST be done using a SOLID fill mode. */
    cli_parse::states->render_context->SetFillMode (e3dRenderStates::Solid);

    std::vector<Underlight::Sector*>::const_iterator sector   = room->sectors.begin ();
    std::vector<Underlight::Sector*>::const_iterator last_sec = room->sectors.end   ();

    while (eSTL::IterValid (sector, last_sec)) {
      if ((*sector) != NULL) {
        room->vcache->Bind   ();
        room->vcache->Enable ();

        DrawPicking (room, (*sector));

        room->vcache->Disable ();
        room->vcache->Unbind  ();
      }

      ++sector;
      continue;
    }

#ifdef HAS_D3D
    if (dev) {
      dev->SetRenderState (D3DRS_FILLMODE, D3DFILL_SOLID);
      dev->SetRenderState (D3DRS_ZFUNC,    D3DCMP_LESSEQUAL);
      dev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW);
    }
#endif
    /* TODO: Use a state block / push/pop mechanism.... */
    /* Restore the normal wireframe/solid fill behaviour. */
    cli_parse::states->render_context->SetFillMode  (states->r_wireframe ? e3dRenderStates::Wireframe : e3dRenderStates::Solid);

    return;
  }

#ifdef HAS_D3D
  if (dev)
    dev->SetRenderState (D3DRS_CULLMODE, (states->r_wireframe ? D3DCULL_NONE : D3DCULL_CW));
#endif

/*
   cli_parse::states->render_context->SetCullFaces (states->r_wireframe ?
                     e3dRenderStates::False :
         e3dRenderStates::True);
*/


  room->vcache->Bind   ();
  room->vcache->Enable ();

  // When the fillmode is solid (or triangle strips are disabled),
  //   we can use the optimized room caches.
  if ((! states->r_wireframe) || (! states->r_triangle_strips)) {
    e3dVector3 light_pos;

    if (! cli_parse::GetNumProjectiles ())
      light_pos = (cli_parse::states->cl_player.Camera ()->GetPos ());
    else
      light_pos = (cli_parse::GetProjectile (cli_parse::GetNumProjectiles () - 1)->origin);

    /* This is essentially the same thing as a depth first pass... consider
         collapsing the code (r_depth_first) below. */
    if (cli_parse::states->r_shadows)
      ShadowPass_Build (room, time, light_pos);

    /*** NOTE: Depth-First would work better if the current room were
                 always drawn first. ***/

    /* Draw a Depth First Pass, if true. */
    if (states->r_depth_first) {
      /* Remember Texturing State. */
      const bool tex       = states->r_tex;
      const bool tex_align = states->r_tex_align;

      /* Disable Texturing. */
      states->r_tex_align = false;
      states->r_tex       = false;
      ApplyTexture     (0);
      DrawDepthBatches (room);

      /* Restore Texturing. */
      states->r_tex       = tex;
      states->r_tex_align = tex_align;
      //glDepthMask (GL_FALSE);
    }

    // Static Batches First
    DrawStaticBatches (room, room->batches, time);

    if (room->animated_batches.size ()) {
      // Animated Batches Next
      DrawAnimatedFlatBatches (room, room->animated_batches, time, ms);

      // Batches With Animated Illumination
      DrawAnimatedIlluminationBatches (room,
                                         room->animated_batches,
                                           time,
                                             light_mat);
    }

    if (cli_parse::states->r_shadows)
      ShadowPass_Cast (room, time, light_pos);
  } else {
    // Otherwise we have to render on a sector-by-sector basis, so as not to
    //  display the lines linking degenerate triangles.
    std::vector<Underlight::Sector*>::const_iterator sector   = room->sectors.begin ();
    std::vector<Underlight::Sector*>::const_iterator last_sec = room->sectors.end   ();

    while (eSTL::IterValid (sector, last_sec)) {
///        DrawPicking ((*room), (*sector));

      DrawStaticBatches               (room, (*sector)->batches, time);
      DrawAnimatedFlatBatches         (room, (*sector)->batches, time, ms);
      DrawAnimatedIlluminationBatches (room, (*sector)->batches, time, light_mat);

      ++sector;
    }
  }

  room->vcache->Disable ();
  room->vcache->Unbind  ();

/*
  if (room->id == cli_parse::states->cl_cam_room &&
      room->background) {
    draw_background (room->background);
  }
*/

  DrawActors (room, time, light_mat);

// TODO: Re-work for self-shadowing sprites...
/*
    if (cli_parse::states->r_shadows) {
      e3dVector3 light_pos (cli_parse::states->cl_player.Camera ()->GetPos ());

      // Apply shadows to the actors...
      BeginShadowPostPass (light_pos);

        //glEnable (GL_POLYGON_OFFSET_FILL);
        //glPolygonOffset (1.2f, -5.0f);

//        glCullFace (GL_BACK);

        DrawShadowActors  ((*room), time, false);

        //glDisable (GL_POLYGON_OFFSET_FILL);

      EndShadowPostPass ();
    }
*/
}


#include "../prototype/deferred_pipeline.h"
#include "../states.h"
#include "../window.h"

eTB_DeferredPipeline deferred;

void
Underlight::Draw (Level* level, e3dRenderContext* rc)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  /* Reset the batch processor, so we can get an accurate measure of the
       geometry rate. */
  cli_parse::batch_processor.Reset ();

  /* Do not draw anything (on the cli_parse backend). */
  if (states->r_null)
    return;

  if ((states->r_deferred_mode > 0 && (! states->cl_need_pick)) || states->r_deferred_mode == 2) {
//#ifdef GHETTO_MODE7
#if 0
    static eTimer timer;
    timer.tick ();

    const int ms   = timer.milliseconds ();
    static int last_rescale = 0;

    static int op = 0;

    const float clamp_max = 0.75f;//0.95f;
    const float clamp_min = 0.15f;//0.005f;
    const int   timescale = 66;//0;

    if (last_rescale < ms - timescale && states->r_deferred_scale <= clamp_max) {
      last_rescale = ms;

      //char command [64];
      //sprintf (command, "r_deferred_scale %2.1f", states->r_deferred_scale + 0.01f);

      //::states->command->ProcessCommandLine (command);
      if (op == 0)
        states->r_deferred_scale *= 2.0f;
      else
        states->r_deferred_scale /= 2.0f;

      if (states->r_deferred_scale > clamp_max) {
        states->r_deferred_scale = clamp_max;
        op = 1;
      }

      if (states->r_deferred_scale < clamp_min) {
        states->r_deferred_scale = clamp_min;
        op = 0;
      }
    }
#endif

    // This will take care of initializing (and re-initializing)
    //   the FBO and render buffers when the viewport dimensions change.
    deferred.resize (::states->window->viewport.Width  (),
                     ::states->window->viewport.Height ());
    deferred.start ();

    glDisable (GL_BLEND);
  }

  //InitDeferredPipeline ();
  //BeginDeferredDraw    ();

#ifdef HAS_D3D
  if (! e3dDirect3D::IsDevicePresent ()) {
#endif
    glMatrixMode  (GL_MODELVIEW);
    glLoadMatrixf (cli_parse::states->cl_player.Camera ()->GetModelview ());
#ifdef HAS_D3D
  }
#endif

  static eTimer timer;

  // TODO: We should use one or more system-wide timers...
  if (cli_parse::states->cl_pause)
    timer.pause (1); // Stop the timer from ticking.
  else
    timer.pause (0); // Resume ticking

  /* ^^^ The calls to pause above will do nothing if the
           timer is already in the requested state, so
             calling the function every frame is acceptable. */

  timer.tick ();

  const float time = timer.time ();
  const float ms   = time * 1000.0f;

  /* 07/28/2011 - (ms) originally used timer.milliseconds (...),
                    but this function is not affected by the timer's
                      pause state.  Multiplying the time by 1000.0f
                                      SHOULD give the desired effect... */

  static D3DMATERIAL9  light_mat;
  static D3DCOLORVALUE diffuse;
  static D3DCOLORVALUE ambient;

  float light_level = AnimateLight (ms) / cli_parse::states->r_light_scale;
  {
#define GLOW_MAPS
#ifdef GLOW_MAPS
  // Ambient light > 1.0 will create a nice bloom effect during
      // post-processing...
    light_level += 1.01f;
#endif

    diffuse.r = diffuse.g = diffuse.b = diffuse.a = 1.0f;

    light_mat.Diffuse = diffuse;

    ambient.r = ambient.g = ambient.b = light_level;
    ambient.a = 1.0f;

    light_mat.Ambient = ambient;
  }

//  eTB_LightCam light_cam (cli_parse::states->cl_player.Camera ()->GetPos (), 45.0f, 1.0f, 1.0f, 8000.0f);

//  light_cam.setLook (e3dVector3 (1.0f, 0.0f, 0.0f));
//  light_cam.setUp   (e3dVector3 (0.0f, 0.0f, 1.0f));
//  light_cam.setRight (e3dVector3 (0.0f, 1.0f, 0.0f));

//  light_cam.updateViewProjMat ();

//  glMatrixMode (GL_MODELVIEW);
//  glLoadMatrixf (light_cam.getViewMat ().m);
//  glMatrixMode (GL_PROJECTION);
//  glLoadMatrixf (light_cam.getProjMat ().m);

////  e3dLoadIdentity  ();

#ifdef HAS_D3D
  LPDIRECT3DDEVICE9 dev = NULL;
  
  if (e3dDirect3D::IsDevicePresent ()) {
    dev = e3dDirect3D::GetDev ();

    dev->SetRenderState (D3DRS_FILLMODE, (states->r_wireframe ? D3DFILL_WIREFRAME : D3DFILL_SOLID));
    dev->SetRenderState (D3DRS_ZFUNC,    (states->r_wireframe ? D3DCMP_ALWAYS     : D3DCMP_LESSEQUAL));
    dev->SetRenderState (D3DRS_CULLMODE, (states->r_wireframe ? D3DCULL_NONE      : D3DCULL_CW));
  } else {
#endif
    cli_parse::states->render_context->SetFillMode  (states->r_wireframe ? e3dRenderStates::Wireframe : e3dRenderStates::Solid);
    cli_parse::states->render_context->SetFrontFace (e3dRenderStates::CounterClockwise);
//    cli_parse::states->render_context->SetFrontFace (e3dRenderStates::Clockwise);

    cli_parse::states->render_context->SetCullFaces (e3dRenderStates::True);
    cli_parse::states->render_context->SetCullSide  (e3dRenderStates::Back);
#ifdef HAS_D3D
  }
#endif

  if ((! states->cl_need_pick) && states->r_fog)
    begin_fog (0x11111111, NULL);//D3DFOG_LINEAR);

//  begin_fog (0x11111111, D3DFOG_LINEAR);

  unsigned int room_num = 0;

  /* NOTE: cl_cam_room is an index, not a room ID!!! */
  Room* cam_room = level->rooms [states->cl_cam_room];
                 //level->GetRoom (states->cl_cam_room);

  /* Draw the camera's room first - this allows the depth first
       pass to eliminate obscured rooms as efficiently as possible. */
  if (cam_room != NULL && cam_room->vcache != NULL) {
    /* Don't draw the background if texture alignment debugging is enabled. */
    if ((! cli_parse::states->r_tex_align)) {
      if (cam_room->background == 0) {
        glDisable (GL_TEXTURE_2D);
        DrawBackground (cam_room->background, 0.0, 0.0, 0.0, 1.0);
      }
      else
        DrawBackground (cam_room->background);
      /* Some rooms have a background of 0, which is invalid... do not attempt
           to draw a background for these rooms. */
    }

    DrawRoom (cam_room, time, ms, light_mat);
  }

  /* Draw the rest of the rooms. */
  if (states->cl_all_rooms) {
    std::vector<Room*>::const_iterator room = level->rooms.begin ();
    std::vector<Room*>::const_iterator end  = level->rooms.end   ();

    while (eSTL::IterValid (room, end)) {
      /* We already drew the camera's room, so skip it... */
      if (room_num++ == states->cl_cam_room) {
        ++room;
        continue;
      }

      // An unloaded room will have no vertex cache...
      if (! (*room)->vcache) {
        ++room;
        continue;
      }

      DrawRoom ((*room), time, ms, light_mat);

      ++room;
    }
  }

  /* Things rendered after this (e.g. text overlays) expect a solid fill mode,
       this is yet another situation where state blocks and a push/pop mechanism
         would be handy... */
  cli_parse::states->render_context->SetFillMode (e3dRenderStates::Solid);
  glBindTexture (GL_TEXTURE_2D, NULL); // Set the current texture to NONE,
                                       //   this helps when using the
                                       //     non-fixed function pipelines.

  /* Last, draw all of the projectiles... */
  DrawProjectiles ();

  if ((! states->cl_need_pick) && states->r_fog)
    end_fog ();

#ifdef HAS_D3D
  if (dev) {
    dev->SetRenderState (D3DRS_FILLMODE, D3DFILL_SOLID);
    dev->SetRenderState (D3DRS_ZFUNC,    D3DCMP_LESSEQUAL);
    dev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW);
  }
#endif

  if ((states->r_deferred_mode == 1 && ((! states->cl_need_pick) || states->cl_player.Teleporting ()))) {
  //if ((states->r_deferred_mode == 1 && (! states->cl_need_pick)) || states->cl_player.Teleporting ()) {
    //EndDeferredDraw ();
    deferred.finish ();

    //DrawDeferredBuffers ();
    deferred.draw_debug ();
  }

  // Implements the fade effect after teleportation in Underlight.
  if (states->cl_player.Teleporting ())
  {
    const int  TELEPORT_TICKS = 50;

    static int teleport_ticks;
    static int last_tick;

    const int milliseconds = (int)ms;

    if (states->r_global_brightness == 1.0f) {
      states->r_global_brightness = 0.0f;
      teleport_ticks              = TELEPORT_TICKS;
      last_tick                   = milliseconds;
    }

    teleport_ticks += (milliseconds - last_tick);
    last_tick       = milliseconds;

    if (teleport_ticks >= TELEPORT_TICKS)
    {
      teleport_ticks = 0;

      const float brightness_rate = 0.05f;

      states->r_global_brightness += brightness_rate;

      if (states->r_global_brightness >= 1.0f) 
      {
        states->cl_player.SetTeleporting (false);
        states->r_global_brightness = 1.0f;
        teleport_ticks              = TELEPORT_TICKS;
      }
    }

    return;
  } else {
    states->r_global_brightness = 1.0f;
  }

#ifdef HAS_D3D
  if (dev) {
    dev->SetRenderState       (D3DRS_ALPHABLENDENABLE, true);
    dev->SetRenderState       (D3DRS_SRCBLEND,         D3DBLEND_SRCALPHA);
    dev->SetRenderState       (D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA);
  }
#endif

  // Draw picking overlays (visually summarize the picked sectors/walls)
  cli_parse::states->render_context->SetFillMode (e3dRenderStates::Solid);

  if (states->cl_picked_sector.surface != -1) {
    DrawSector (states->cl_picked_sector.room, 
                states->cl_picked_sector.surface);

    static signed int last_picked = 0;

    if (last_picked != states->cl_picked_sector.surface) {
      PickingData pick = states->cl_picked_sector;

      if (pick.room    < MAX_ROOMS   && pick.room    > 0 &&
          pick.surface < MAX_SECTORS && pick.surface > 0) {
        Sector* sec = level->GetRoom (pick.room)->GetSector (pick.surface);

        if (sec) {
          eTB_Printf (" > Is %3.1f Kb in size  (Parent: %3.1f Kb)\n",
                          sec->size () / 1024.0f,
                            level->GetRoom (pick.room)->MemSize () / 1024.0f);
          DumpSector (sec);
        }
      }

      last_picked = states->cl_picked_sector.surface;
    }
  }

  if (states->cl_picked_line.surface != -1) {
    DrawLine (states->cl_picked_line.room, 
              states->cl_picked_line.surface);

    static signed int last_picked = 0;

    if (last_picked != states->cl_picked_line.surface) {
      PickingData pick = states->cl_picked_line;

      if (pick.room    < MAX_ROOMS && pick.room    > 0 &&
          pick.surface > MAX_SECTORS && pick.surface > 0) {

        int line_id = pick.surface - MAX_SECTORS;

        Room* room = level->GetRoom (pick.room);

        Line* line = GetLine (room, line_id);

        if (line) {
          DumpLine (line);
        }
      }

      last_picked = states->cl_picked_line.surface;
    }
  }

#ifdef HAS_D3D
  if (dev)
    dev->SetRenderState (D3DRS_ALPHABLENDENABLE, false);
#else
  glDisable (GL_BLEND);
#endif

  /* NOTE: Somehow, sometimes a vcache is not unbound properly...

             This catches the error, but does nothing to track the
                source of the problem :-\ */
  glBindBuffer (GL_ARRAY_BUFFER,         NULL);
  glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, NULL);

  if (states->r_deferred_mode == 2 || states->cl_player.Teleporting ()) {
    //EndDeferredDraw ();
    deferred.finish ();

    //DrawDeferredBuffers ();
    deferred.draw_debug ();
  }
}

D3DMATERIAL9 pick_mat;

void
Underlight::DrawPicking (Room* room, Sector* sector)
{
  // Again, this should never happen, but it does.
  if (room == NULL || sector == NULL)
    return;

#ifdef HAS_D3D
  LPDIRECT3DDEVICE9 dev = NULL;
  
  if (e3dDirect3D::IsDevicePresent ())
    dev = e3dDirect3D::GetDev ();

  if (dev)
    dev->SetRenderState (D3DRS_FILLMODE, D3DFILL_SOLID);
#endif

  std::vector<cli_parse::RenderBatch*>::const_iterator batch      =
      sector->picking_batches.begin ();
  std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
      sector->picking_batches.end   ();

#ifdef HAS_D3D
  if (dev)
    dev->SetRenderState (D3DRS_COLORVERTEX, false);
 #endif

  D3DCOLORVALUE diffuse;
  diffuse.r = 1.0f;
  diffuse.g = 1.0f;
  diffuse.b = 1.0f;
  diffuse.a = 1.0f;

#ifdef HAS_D3D
  if (dev) {
    dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
    dev->SetRenderState (D3DRS_LIGHTING, true);

    dev->SetTexture (0, NULL);
  } else {
#endif
    glDisable            (GL_TEXTURE_2D);
    glDisableClientState (GL_COLOR_ARRAY);
    glEnable             (GL_COLOR_MATERIAL);
    glColorMaterial      (GL_FRONT_AND_BACK, GL_DIFFUSE);
    glColor4f            (1.0f, 1.0f, 1.0f, 1.0f);
#ifdef HAS_D3D
  }
#endif

  while (eSTL::IterValid (batch, last_batch)) {
    PickingData pick ((*batch)->rgbgen);

    pick_mat.Diffuse = diffuse;
    pick_mat.Ambient = pick.rgba ();

#ifdef HAS_D3D
    if (dev)
      dev->SetMaterial (&pick_mat);
    else {
#endif
      glColorMaterial      (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
      glColor4f            (pick_mat.Ambient.r, pick_mat.Ambient.b, pick_mat.Ambient.g, pick_mat.Ambient.a);
#ifdef HAS_D3D
    }
#endif

    cli_parse::batch_processor.Draw (room->vcache, (*batch));

    ++batch;
  }

#ifdef HAS_D3D
  if (dev) {
    dev->SetRenderState (D3DRS_COLORVERTEX,           true);
    dev->SetRenderState (D3DRS_LIGHTING,              false);
    dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
  }
#endif
}

#include "../../Epsilon/Epsilon3D/immediate.h"


void
Underlight::DrawSector (int room_id, int sector_id)
{
  // TODO: This should never happen, but it DOES in OpenGL on my laptop.
  if (room_id < 1 || sector_id < 1)
    return;

  cli_parse::ModuleStates* states = cli_parse::states;

#ifdef HAS_D3D
  LPDIRECT3DDEVICE9 dev = NULL;

  if (e3dDirect3D::IsDevicePresent ())
    dev = e3dDirect3D::GetDev ();
#endif

  static D3DMATERIAL9 flat_mat;
  static D3DMATERIAL9 fwall_mat;
  static D3DMATERIAL9 cwall_mat;

  static D3DCOLORVALUE ambient_flat;
  static D3DCOLORVALUE ambient_fwall;
  static D3DCOLORVALUE ambient_cwall;

  static D3DCOLORVALUE diffuse;
  diffuse.r = 1.0f; diffuse.g = 1.0f; diffuse.b = 1.0f;
  diffuse.a = 0.5f;

  ambient_flat.r = 0.0f; ambient_flat.g = 1.0f; ambient_flat.b = 0.0f;
  ambient_flat.a = 0.5f;

  flat_mat.Diffuse = diffuse;
  flat_mat.Ambient = ambient_flat;

  ambient_fwall.r = 1.0f; ambient_fwall.g = 0.0f; ambient_fwall.b = 0.0f;
  ambient_fwall.a = 0.5f;

  ambient_cwall.r = 0.0f; ambient_cwall.g = 0.0f; ambient_cwall.b = 1.0f;
  ambient_cwall.a = 0.5f;

  fwall_mat.Diffuse = diffuse;
  fwall_mat.Ambient = ambient_fwall;

  cwall_mat.Diffuse = diffuse;
  cwall_mat.Ambient = ambient_cwall;


  std::vector<Room*>::const_iterator room = lvl->rooms.begin ();
  std::vector<Room*>::const_iterator end  = lvl->rooms.end   ();

  while (eSTL::IterValid (room, end)) {
    if ((*room)->id != room_id || (! (*room)->vcache)) {
      ++room;
      continue;
    }

    std::vector<Sector*>::const_iterator sector   = (*room)->sectors.begin ();
    std::vector<Sector*>::const_iterator last_sec = (*room)->sectors.end   ();

    while (eSTL::IterValid (sector, last_sec)) {
      if ((*sector)->id != sector_id) {
        ++sector;
        continue;
      }

      (*room)->vcache->Bind   ();
      (*room)->vcache->Enable ();

#ifdef HAS_D3D
      if (dev) {
        dev->SetRenderState (D3DRS_ALPHABLENDENABLE,      true);
        dev->SetRenderState (D3DRS_FILLMODE,              D3DFILL_SOLID);
        dev->SetRenderState (D3DRS_COLORVERTEX,           false);
        dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
        dev->SetRenderState (D3DRS_LIGHTING,              true);

        dev->SetTexture (0, NULL);
      } else {
#endif
        glColorMaterial      (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        glDisableClientState (GL_COLOR_ARRAY);

        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable    (GL_BLEND);

        glDisable (GL_TEXTURE_2D);
#ifdef HAS_D3D
      }
#endif

      // Draw

      std::vector<cli_parse::RenderBatch*>::const_iterator batch      =
          (*sector)->picking_batches.begin ();
      std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
          (*sector)->picking_batches.end   ();

#ifdef HAS_D3D
      if (dev)
        dev->SetRenderState (D3DRS_ZFUNC, (states->r_wireframe ? D3DCMP_LESS : D3DCMP_EQUAL));
#endif

      while (eSTL::IterValid (batch, last_batch)) {
        if ((*batch)->type != 1 && (*batch)->type != -1) {
#ifdef HAS_D3D
          if (dev) {
            dev->SetMaterial (&wall_mat);
          } else {
#endif
            if ((*batch)->type == -2)
              glColor4f (cwall_mat.Ambient.r, cwall_mat.Ambient.g, cwall_mat.Ambient.b, cwall_mat.Ambient.a);
            else if ((*batch)->type == 2)
              glColor4f (fwall_mat.Ambient.r, fwall_mat.Ambient.g, fwall_mat.Ambient.b, fwall_mat.Ambient.a);

#ifdef HAS_D3D
          }
#endif
        } else {
#ifdef HAS_D3D
          if (dev) {
            dev->SetMaterial (&flat_mat);
          } else {
#endif
            glColor4f (flat_mat.Ambient.r, flat_mat.Ambient.g, flat_mat.Ambient.b, flat_mat.Ambient.a);
#ifdef HAS_D3D
          }
#endif
        }

        cli_parse::batch_processor.Draw ((*room)->vcache, (*batch));

        /// Draw a Wireframe Overlay
        glPushAttrib (GL_ALL_ATTRIB_BITS);//GL_POLYGON_BIT | GL_CURRENT_BIT);
          glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
          glDisable   (GL_BLEND);
          glDisable   (GL_DEPTH_TEST);
          glDisable   (GL_ALPHA_TEST);
          glHint      (GL_LINE_SMOOTH, GL_NICEST);
          glLineWidth (0.5f);
          glColor4f   (0.0f, 0.0f, 0.0f, 1.0f);

          cli_parse::batch_processor.Draw ((*room)->vcache, (*batch));

        glPopAttrib ();

        ++batch;
      }

      (*room)->vcache->Disable ();
      (*room)->vcache->Unbind  ();

#ifdef HAS_D3D
      if (dev) {
        dev->SetRenderState (D3DRS_ZFUNC, D3DCMP_LESS);

        dev->SetRenderState (D3DRS_ALPHABLENDENABLE,      false);
        dev->SetRenderState (D3DRS_COLORVERTEX,           true);
        dev->SetRenderState (D3DRS_LIGHTING,              false);
        dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
      } else {
#endif
        glDisable (GL_BLEND);
#ifdef HAS_D3D
      }
#endif

      break;
    }

    break;
  }
}

void
Underlight::DrawLine (int room_id, int line_id)
{
  //static D3DMATERIAL9 flat_mat;
  static D3DMATERIAL9 wall_mat;

  //static D3DCOLORVALUE ambient_flat;
  static D3DCOLORVALUE ambient_wall;

  static D3DCOLORVALUE diffuse;
  diffuse.r = 1.0f; diffuse.g = 1.0f; diffuse.b = 1.0f;
  diffuse.a = 0.5f;

  /*
  ambient_flat.r = 1.0f; ambient_flat.g = 0.0f; ambient_flat.b = 0.0f;
  ambient_flat.a = 0.5f;

  flat_mat.Diffuse = diffuse;
  flat_mat.Ambient = ambient_flat;
  */

  ambient_wall.r = 1.0f; ambient_wall.g = 0.0f; ambient_wall.b = 1.0f;
  ambient_wall.a = 0.5f;

  wall_mat.Diffuse = diffuse;
  wall_mat.Ambient = ambient_wall;

  std::vector<Room*>::const_iterator room = lvl->rooms.begin ();
  std::vector<Room*>::const_iterator end  = lvl->rooms.end   ();

  while (eSTL::IterValid (room, end)) {
    if ((*room)->id != room_id) {
      ++room;
      continue;
    }

    std::vector<Sector*>::const_iterator sector   = (*room)->sectors.begin ();
    std::vector<Sector*>::const_iterator last_sec = (*room)->sectors.end   ();

    while (eSTL::IterValid (sector, last_sec)) {
      std::vector<cli_parse::RenderBatch*>::const_iterator batch      =
          (*sector)->picking_batches.begin ();
      std::vector<cli_parse::RenderBatch*>::const_iterator last_batch =
          (*sector)->picking_batches.end   ();

      while (eSTL::IterValid (batch, last_batch)) {
        // Walls only ...
        if ((*batch)->type != 1 && (*batch)->type != -1) {
          if (PickingData ((*batch)->rgbgen).surface == PickingData ((char)room_id, true, (short)line_id).surface) {

            (*room)->vcache->Bind ();
            (*room)->vcache->Enable ();

#ifdef HAS_D3D
      if (dev) {
        dev->SetRenderState (D3DRS_ALPHABLENDENABLE,      true);
        dev->SetRenderState (D3DRS_FILLMODE,              D3DFILL_SOLID);
        dev->SetRenderState (D3DRS_COLORVERTEX,           false);
        dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
        dev->SetRenderState (D3DRS_LIGHTING,              true);

        dev->SetTexture (0, NULL);
      } else {
#endif
        glColorMaterial      (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        glDisableClientState (GL_COLOR_ARRAY);

        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable    (GL_BLEND);

        glDisable (GL_TEXTURE_2D);
#ifdef HAS_D3D
      }
#endif

#ifdef HAS_D3D
          if (dev) {
            dev->SetMaterial (&wall_mat);
          } else {
#endif
            if ((*batch)->type == -2)
             glColor4f (0.5f, 1.0f, 0.0f, 0.5f);
            else
              glColor4f (0.0f, 1.0f, 0.5f, 0.5f);
//            glColor4f (wall_mat.Ambient.r, wall_mat.Ambient.g, wall_mat.Ambient.b, wall_mat.Ambient.a);
#ifdef HAS_D3D
          }
#endif

          cli_parse::batch_processor.Draw ((*room)->vcache, (*batch));
          //(*room)->vcache->DrawIndexed ((*batch)->primitive_type, (*batch)->icache, 0, (unsigned int)(*batch)->icache->GetSize ());
          (*room)->vcache->Disable ();
          (*room)->vcache->Unbind  ();


#ifdef HAS_D3D
      if (dev) {
        dev->SetRenderState (D3DRS_ZFUNC, D3DCMP_LESS);

        dev->SetRenderState (D3DRS_ALPHABLENDENABLE,      false);
        dev->SetRenderState (D3DRS_COLORVERTEX,           true);
        dev->SetRenderState (D3DRS_LIGHTING,              false);
        dev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
      } else {
#endif
        glDisable (GL_BLEND);
#ifdef HAS_D3D
      }
#endif

            sector = last_sec; // Keep looking for batches for this line,
                               //  but only in this sector.
//            break;

	  }
        }

        ++batch;
      }

      if (sector != last_sec)
        ++sector;
    }

    sector = (*room)->sectors.begin ();

    while (eSTL::IterValid (sector, last_sec)) {
      if ((*sector) != NULL) {
        Line* line = (*sector)->GetLine (line_id - MAX_SECTORS);//PickingData ((short)room_id, true, (short)line_id).surface);

        if (line != NULL && (! line->generated)) {
          glPointSize (10.0f);
          glBegin (GL_POINTS);
            glColor4f  (1.0f, 1.0f, 1.0f, 1.0f);
            glVertex3f (line->x1 (), line->y1 (), 0.0f);
            glVertex3f (line->x2 (), line->y2 (), 0.0f);
            glVertex3f (line->x1 () + 100.0f * -line->SINE, line->y1 () + 100.0f * line->COS, 0.0f);
            glVertex3f (line->x2 () + 100.0f * -line->SINE, line->y2 () + 100.0f * line->COS, 0.0f);
          glEnd ();
        }
      }

      ++sector;
    }


    break;
  }
}















void begin_fog (DWORD Color, DWORD Mode)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  float Start   = 10000.0f,  // For linear mode
        End     = 30000.0f,
        Density = 0.06f;  // For exponential modes

 #ifdef DIRECT3D
  // Enable fog blending.
  e3dDirect3D::dev->SetRenderState (D3DRS_FOGENABLE, true);

  // Set the fog color.
  e3dDirect3D::dev->SetRenderState (D3DRS_FOGCOLOR, Color);

  // Set fog parameters.
  if (D3DFOG_LINEAR == Mode)
  {
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGENABLE,      true);
    e3dDirect3D::dev->SetRenderState (D3DRS_RANGEFOGENABLE, true);
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGCOLOR,       D3DCOLOR_XRGB (128, 128, 128));
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGVERTEXMODE,  D3DFOG_EXP2);

    e3dDirect3D::dev->SetRenderState (D3DRS_FOGTABLEMODE, Mode);
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGSTART,     *(DWORD *)(&Start));
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGEND,       *(DWORD *)(&End));
  }
  else
  {
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGTABLEMODE, Mode);
    e3dDirect3D::dev->SetRenderState (D3DRS_FOGDENSITY,   *(DWORD *)(&Density));
  }
#else
  glFogf   (GL_FOG_START,   states->r_fog_start);
  glFogf   (GL_FOG_END,     states->r_fog_end);
  glFogf   (GL_FOG_DENSITY, states->r_fog_density);
  glFogi   (GL_FOG_MODE,    GL_EXP2);
  glEnable (GL_FOG);
#endif
}

void end_fog (void)
{
#ifdef DIRECT3D
  e3dDirect3D::dev->SetRenderState (D3DRS_FOGENABLE, false);
#else
  glDisable (GL_FOG);
#endif
}

/*
void display_screenspace_GL (void)
{
  glShadeModel (GL_FLAT);
  glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
  
  glMatrixMode     (GL_MODELVIEW);
    glLoadIdentity ();
  
    gluLookAt (0.0, 0.0, 5.0, 
               0.0, 0.0, 0.0,
               0.0, 1.0, 0.0);
  
    glMatrixMode     (GL_PROJECTION);
      glLoadIdentity ();
  
      glOrtho (-50.0, 50.0,
               -50.0, 50.0,
               0.001, 100);
  
      glClear (GL_COLOR_BUFFER_BIT);
  
      //glColor3f (1.0f, 1.0f, 1.0f);
  
      glViewport (0.0, 0.0,
                  win1.viewport.Width (),
                    win1.viewport.Height ());
  
      glBegin (GL_LINES);
        glColor3f  (1.0f, 0.0f, 0.0f);
        glVertex2f (-50.0f, -50.0f);
        glVertex2f ( 50.0f,  50.0f);


        glColor3f  (0.0f, 1.0f, 0.0f);
        glVertex2f (-50.0f,  50.0f);
        glVertex2f ( 50.0f, -50.0f);


        glColor3f  (0.0f, 0.0f, 1.0f);
        glVertex2f (0.0f,  -50.0f);
        glVertex2f (0.0f,   50.0f);


        glColor3f  (1.0f, 0.0f, 1.0f);
        glVertex2f (-50.0f, 0.0f);
        glVertex2f ( 50.0f, 0.0f);
      glEnd ();
  
  glFlush ();
} 
*/




e3dVertex aFlatVertices [512];

void
DrawAnimatedFlat (e3dVertexCache* vcache, e3dIndexCache* icache, int tex_id, float x_offset, float y_offset, Underlight::Surface* surface, Underlight::Sector* sector)
{
#if 0
  if (! surface)
    return;

  e3dVertex* pVert = aFlatVertices;

  int iVerts = icache->GetSize ();
  for (int i = 0; i < iVerts; i++) {
    e3dVertex vert = vcache->GetVertex (icache->GetIndex (i));

    aFlatVertices [i].pos [0] = vert.pos [0];
    aFlatVertices [i].pos [1] = vert.pos [1];
    aFlatVertices [i].pos [2] = vert.pos [2];

    aFlatVertices [i].color [0] = vert.color [0];
    aFlatVertices [i].color [1] = vert.color [1];
    aFlatVertices [i].color [2] = vert.color [2];
    aFlatVertices [i].color [3] = vert.color [3];

    float u;
    float v;

    surface->tex_coord_offset_at (aFlatVertices [i].pos [0],
                                  aFlatVertices [i].pos [1],
                                  tex_id,
                                  sector,
                                  true,
                                  u,
                                  v,
                                  x_offset,
                                  y_offset);

    aFlatVertices [i].st [0] = vert.st [0] + u;
    aFlatVertices [i].st [1] = vert.st [1] + v;
  }

  //
  // Direct3D Specific
  //
  LPDIRECT3DDEVICE9 dev = e3dDirect3D::GetDev ();

  /*
  dev->SetFVF               ( D3DFVF_XYZRHW  |
                              D3DFVF_DIFFUSE |
                              D3DFVF_TEX1 );
  */

  /*
  dev->SetRenderState       (D3DRS_ALPHABLENDENABLE, true);
  dev->SetRenderState       (D3DRS_SRCBLEND,         D3DBLEND_SRCALPHA);
  dev->SetRenderState       (D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA);

  dev->SetTextureStageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

  dev->SetTexture           (0, m_pCursorTexture);
  */

  dev->DrawPrimitiveUP      (D3DPT_TRIANGLELIST, iVerts / 3, aFlatVertices, sizeof (e3dVertex));

  //dev->SetRenderState       (D3DRS_ALPHABLENDENABLE, false);
#endif
} //DrawCur


#include "effect.h"

#include "../../Epsilon/debug.h"

/* NOTE:
     This code is very messy, because it is a pre-liminary step inbetween drawing
       actors one-at-a-time and using a batch-rendering system.

     In the end, actors will be batched by texture.

       To do this most efficiently, a vertex shader will be used to setup the
         position of the 4 vertices...
*/
void
DrawBillboard (cli_parse::Actor* actor, float time, bool texture = true)
{
  /* This changes the vertex array ptrs... - We should REALLY stop doing it this way! */
////  glPushClientAttrib (GL_CLIENT_VERTEX_ARRAY_BIT);
////  glPushAttrib       (GL_ALL_ATTRIB_BITS);

  //eTB_Printf ("Bitmap: %d\n", ((cli_parse::Ornament *)actor)->GetBitmap ());

  static std::vector<e3dVertex> verts;
  static std::vector<unsigned short> indices;

  unsigned int num_verts   = 4;
  unsigned int num_indices = (num_verts / 4) * 6;
  unsigned int num_actors  = 1;

  if (indices.size () < num_indices) {
    indices.resize (num_indices);

    unsigned short base_idx = 0;

    for (unsigned int i = 0; i < num_indices; i += 6, base_idx += 4) {
      indices [i    ] = base_idx + 0;
      indices [i + 1] = base_idx + 1;
      indices [i + 2] = base_idx + 2;
      indices [i + 3] = base_idx + 0;
      indices [i + 4] = base_idx + 2;
      indices [i + 5] = base_idx + 3;
    }
  }

  if (verts.size () < num_verts) {
    verts.resize (num_verts);

    for (unsigned int i =  0; i < num_verts; i += 4) {
       verts [i].st [0] = 1.0f;
       verts [i].st [1] = 0.0f;

       verts [i + 1].st [0] = 1.0f;
       verts [i + 1].st [1] = 1.0f;

       verts [i + 2].st [0] = 0.0f;
       verts [i + 2].st [1] = 1.0f;

       verts [i + 3].st [0] = 0.0f;
       verts [i + 3].st [1] = 0.0f;

       *((DWORD *)verts [i    ].color) = 0x00ffffff;
       *((DWORD *)verts [i + 1].color) = 0x00ffffff;
       *((DWORD *)verts [i + 2].color) = 0x00ffffff;
       *((DWORD *)verts [i + 3].color) = 0x00ffffff;
    }
  }

  glVertexPointer (3, GL_FLOAT, sizeof (e3dVertex), &verts [0].pos);
  //glColorPointer  (4, GL_UNSIGNED_BYTE, sizeof (e3dVertex), &verts [0].color);
  glTexCoordPointer (2, GL_FLOAT, sizeof (e3dVertex), &verts [0].st);

  glEnableClientState  (GL_VERTEX_ARRAY);
  glEnableClientState  (GL_TEXTURE_COORD_ARRAY);
  glDisableClientState (GL_COLOR_ARRAY);

  e3dCamera* cam    = cli_parse::states->cl_player.Camera ();
  e3dVector3 vRight = cam->right_vec ();
  /// Camera's normal vector...
  ////const e3dVector3 diff = cam->fwd_vec ();

  int idx        =  0;
  int active_tex = -1;

  for (int i = 0; i < 1/*actors.size ()*/; i++) {
  const cli_parse::Ornament* ornament = (cli_parse::Ornament *)actor;
  const float size = Underlight::effects->GetBitmap (ornament->GetBitmap ())->h;
  const float mag  = 0.5f;

  //height *= 2.0f;

  // C  = Center
  // s  = Size
  // m  = Magnitude
  // U' = Up Vector
  // R' = Right Vector
  // D' = View Vector
  //
  // v0 = C - s * m (U' - R');
  // v1 = C + s * m (U' + R');
  // v2 = C + s * m (U' - R');
  // v3 = C - s * m (U' + R');
  //
  // n0 = n1 = n2 = n3 = D'

  //
  // Don't need the up vector for the type of billboarding Underlight uses.
  //

  const float origin [3] = { actor->GetX (), actor->GetY (), actor->GetZ () };

  float* vert_pos = &verts [idx].pos [0];

  vert_pos [0] = origin [0] - (size * mag) * (-vRight.x);
  vert_pos [1] = origin [1] - (size * mag) * (-vRight.y);
  vert_pos [2] = origin [2];

  vert_pos = &verts [++idx].pos [0];

  vert_pos [0] = origin [0] + (size * mag) * (vRight.x);
  vert_pos [1] = origin [1] + (size * mag) * (vRight.y);
  vert_pos [2] = origin [2] + ornament->GetExtent ();

  vert_pos = &verts [++idx].pos [0];

  vert_pos [0] = origin [0] + (size * mag) * (-vRight.x);
  vert_pos [1] = origin [1] + (size * mag) * (-vRight.y);
  vert_pos [2] = origin [2] + ornament->GetExtent ();

  vert_pos = &verts [++idx].pos [0];

  vert_pos [0] = origin [0] - (size * mag) * (vRight.x);
  vert_pos [1] = origin [1] - (size * mag) * (vRight.y);
  vert_pos [2] = origin [2];

  idx++;
      if (texture) {
        const int tex = ornament->GetBitmap ();

        if (ornament->GetFrames () > 1) { 
          int frame = ((int)(time * 10.0f) % ornament->GetFrames ());
          ApplyAnimatedEffect (tex, frame);
        } else {
          ApplyEffectTexture (tex);
        }
      }
  }

//    if (texture != bound_texture) {
    if (true) {
      /* Sometimes we don't want to texture a billboard... */
      /*
      if (texture) {
        const int tex = ornament->GetBitmap ();

        if (ornament->GetFrames () > 1) { 
          int frame = ((int)(time * 10.0f) % ornament->GetFrames ());
          ApplyAnimatedEffect (tex, frame);
        } else {
          ApplyEffectTexture (tex);
        }
      }
      */

      /* End the array of actors... */
      glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (unsigned short*)&indices [0]);

      num_actors = 0;
    }

////    glPopAttrib ();
////    glPopClientAttrib ();
}


void
draw_background (int tex)
{
#ifndef USE_ENHANCED_BACKGROUND
  return;
#else
  struct OverlayVertex
  {
    float         pos   [4];
    unsigned char color [4];
    float         st    [2];
  };

  OverlayVertex aOverlayVertices [4];

  OverlayVertex* pVert = aOverlayVertices;

  //float color = brightness;

  for (int i = 0; i < 4; i++) {
            aOverlayVertices [i].pos [2] = 1.0f;
            aOverlayVertices [i].pos [3] = 1.0f; // w = 1.0f
    (DWORD&)aOverlayVertices [i].color   = D3DCOLOR_COLORVALUE (1.0f, 1.0f, 1.0f, 1.0f);
  }

  static float last_change = 0.0f;
  static eTimer timer;
  timer.tick ();
  //float time = timer.milliseconds ();

  const int num_panels = 5;

  e3dCamera* cam = cli_parse::states->cl_player.Camera ();
  e3dVector3 vec = cam->fwd_vec ();
  float x = vec.x;
  float y = vec.y;
  float z = vec.z;

  float angle_rad_z = atan2 (x, y);

  if (angle_rad_z < 0.0f)
    angle_rad_z += (2 * D3DX_PI);

  float panel_len   = 1.0f / num_panels;
  float panel_start = -(angle_rad_z / (2 * D3DX_PI));
  float panel_end   = panel_start + panel_len;

  int height     = 300;//480;//600;//300;
  int win_height = g_Window->win->getHeight ();

  float top    = (win_height / 2.0f) + (height / 2.0f);
  float bottom = (win_height / 2.0f) - (height / 2.0f);


  /*
  //vec = cam->up_vec ();
  //x = vec.x;
  //y = vec.y;  //z = vec.z;

  float angle_rad_x = asin (-z);//atan2 (x, z);

  
  printf ("Angle 0: %f\n", angle_rad_x);

  if (angle_rad_x < 0.0f)
    angle_rad_x = angle_rad_x += (2 * D3DX_PI);

  angle_rad_x /= (2.0f * D3DX_PI);
  printf ("Angle 1: %f\n", angle_rad_x);

  top    += (angle_rad_x) * (height / 2.0f);
  bottom += (angle_rad_x) * (height / 2.0f);

  printf ("Angle 2: %f\n", angle_rad_x);
  */


  pVert->pos [0] = 0.0f;
  pVert->pos [1] = bottom;//0.0f;
  pVert->st  [0] = panel_start;
  pVert->st  [1] = 0.0f;

  pVert++;

  pVert->pos [0] = 0.0f;
  pVert->pos [1] = top;//g_Window->viewport.Height ();
  pVert->st  [0] = panel_start;
  pVert->st  [1] = -1.0f;

  pVert++;

  pVert->pos [0] = (float)g_Window->viewport.Width ();
  pVert->pos [1] = bottom;//0.0f;
  pVert->st  [0] =  panel_end;
  pVert->st  [1] =  0.0f;

  pVert++;

  pVert->pos [0] = (float)g_Window->viewport.Width  ();
  pVert->pos [1] = top;//g_Window->viewport.Height ();
  pVert->st  [0] =  panel_end;
  pVert->st  [1] = -1.0f;

/// TODO: Port Me
#ifdef HAS_D3D
  //
  // Direct3D Specific
  //
  if (! e3dDirect3D::IsDevicePresent ())
    return;

  LPDIRECT3DDEVICE9 dev = e3dDirect3D::GetDev ();

  dev->SetFVF               ( D3DFVF_XYZRHW  |
                              D3DFVF_DIFFUSE |
                              D3DFVF_TEX1 );

  dev->SetSamplerState (0, D3DSAMP_ADDRESSU,  D3DTADDRESS_MIRRORONCE);
  dev->SetSamplerState (0, D3DSAMP_ADDRESSV,  D3DTADDRESS_MIRRORONCE);

  //dev->SetRenderState       (D3DRS_ZENABLE,          D3DZB_FALSE);
  //dev->SetRenderState       (D3DRS_ZFUNC,            D3DCMP_ALWAYS);
  dev->SetRenderState       (D3DRS_ALPHABLENDENABLE, false);
  dev->SetRenderState       (D3DRS_ZWRITEENABLE,     false);
  //dev->SetRenderState       (D3DRS_SRCBLEND,         D3DBLEND_ZERO);
  //dev->SetRenderState       (D3DRS_DESTBLEND,        D3DBLEND_SRCALPHA);

  //dev->SetTexture           (tex, NULL);
  //ApplyTexture (MAX_TEXTURES);
    ApplyEffectTexture (tex);
  dev->DrawPrimitiveUP      (D3DPT_TRIANGLESTRIP, 2, aOverlayVertices, sizeof (OverlayVertex));

  //dev->SetRenderState       (D3DRS_ALPHABLENDENABLE, false);
  dev->SetRenderState       (D3DRS_ZWRITEENABLE,     true);
  //dev->SetRenderState       (D3DRS_ZFUNC,            D3DCMP_LESSEQUAL);
  //dev->SetRenderState       (D3DRS_ZENABLE,          D3DZB_TRUE);

    dev->SetSamplerState (0, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP);
  dev->SetSamplerState (0, D3DSAMP_ADDRESSV,    D3DTADDRESS_WRAP);
#endif /* HAS_D3D */
#endif /* USE_ENHANCED_BACKGROUND */
}